package msu.ml.presentation;

import weka.core.*;
import java.util.*;
import java.io.*;
import javax.imageio.*;
import java.awt.image.*;
import java.awt.*;

public class SimilarityMatrix
{
   public static int SAMPLE_SIZE = 100;

   public static void saveImage(Instances data, int [] clusters, 
      int numClusters, String file, int width, int height)
   {
      SimilarityMatrix.saveImage(data, clusters, numClusters, file,
         width, height, true);
   }

   public static void saveImage(Instances data, int [] clusters, 
      int numClusters, String file, int width, int height, boolean color)
   {
      try
      {
         int size = numClusters * SAMPLE_SIZE;
         width = Math.max(size * 2, width);
         height = Math.max(size * 2, height);
         int legendWidth = 200;
         int fontSize = 72;

         BufferedImage image = new BufferedImage(width + legendWidth * 2, 
               height, BufferedImage.TYPE_INT_ARGB);
         Graphics2D g = image.createGraphics();
         g.setColor(Color.white);
         g.fillRect(0, 0, image.getWidth(), image.getHeight());

         int boxWidth = width / size;
         int boxHeight = height / size;

         double [][] matrix = SimilarityMatrix.getMatrix(data, clusters, numClusters);
         for(int i = 0; i < size; i++)
         {
            for(int j = 0; j < size; j++)
            {
               double sim = matrix[i][j];
               sim = Math.min(1, Math.max(0, sim));
               if(color)
               {
                  if(sim <= 0.5)
                  {
                     sim = sim * 2;
                     g.setColor(new Color(0.0f, (float)sim, (float)(1 - sim)));
                  }
                  else
                  {
                     sim = (sim - .5) * 2;
                     g.setColor(new Color((float)sim, (float)(1 - sim), 0.0f));
                  }
               }
               else
               {
                  float fsim = 1.0f - (float)sim;
                  g.setColor(new Color(fsim, fsim, fsim));
               }
               g.fillRect(j * boxWidth, i * boxHeight, boxWidth, boxHeight);
            }
         }              

         //Draw Legend
         g.setPaint(new GradientPaint(width, 0, Color.black, width, height, Color.white));
         g.fillRect(width, 0, legendWidth, height);

         Font f = g.getFont();
         g.setFont(new Font(f.getName(), f.getStyle(), fontSize));

         g.setColor(Color.black);
         for(double i = 1.0; i >= 0.0; i -= .1)
         {
            int loc = height - (int)((double)height * i);

            if(loc == 0)
               loc += fontSize;

            if(loc == height)
               loc -= 10;

            int tickHeight = loc - (fontSize / 2);

            g.fillRect(width + (legendWidth / 2), tickHeight, (legendWidth / 2) + 5, 5);
            g.drawString(String.format("%1$.1f", i), width + legendWidth + 10, loc);
         }
         
         File imageFile = new File(file);
         ImageIO.write(image, "png", imageFile);
      }
      catch(Exception e)
      {
         System.out.println(e);
         e.printStackTrace();
      }
   }

   public static double [][] getMatrix(Instances data, int [] clusters, int numClusters)
   {
      int size = numClusters * SAMPLE_SIZE;
      Instance [] samples = new Instance[size];
      Random random = new Random(System.currentTimeMillis());

      for(int i = 0, j = 0; i < numClusters; i++)
      {
         int count = 0;
         while(count < SAMPLE_SIZE)
         {
            int idx = random.nextInt(clusters.length);
            if(clusters[idx] == i)
            {
               samples[j++] = data.instance(idx);
               count++;
            }
         }
      }
      
      double [][] proximity = new double [size][size];
      EuclideanDistance eu = new EuclideanDistance(data);
      
      double maxDist = 0;
      for(int i = 0; i < size; i++)
      {
         for(int j = 0; j < size; j++)
         {
            double dist = eu.distance(samples[i], samples[j]);
            proximity[i][j] = dist;
            if(dist > maxDist)
               maxDist = dist;
         }
      }

      //s = 1 - (d - min_d) / (max_d - min_d)
      for(int i = 0; i < size; i++)
         for(int j = 0; j < size; j++)
            proximity[i][j] = 1 - (proximity[i][j] / maxDist);

      return proximity;
   }

}
